﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;
using System.Net;

namespace NotificationServer.BusinessLayer.UtilityClasses
{
    class GCM
    {
        //http://developer.android.com/guide/google/gcm/gcm.html#server

        private string _senderId = "667132395722";        
        private string _googleAppId = "AIzaSyDP6ojoRPeiFk_-oAWX7Sx7b5Qf5woLaq0";        
        private const string GCM_SERVER_URI = "https://android.googleapis.com/gcm/send";
        

        public bool SendNotification(string notificationId, string deviceId, string message)
        {
            
            //device_Id = "APA91bFHvsCt0jlXTcskq3pSAa8DQsSQlDSeAT9VB1sByU4Xe9Ze_uboSpvN2I3En7ZlzsFTUTWCVkoRhwMSMp54LWTQYvy7Ke6203pJwCicULzHx2NKpcziD9ydyEnE8lzRZaLmemKMkwCwlrdfY35Xcvh3vwd98M";
            //device_Id = "APA91bFHvsCt0jlXTcskq3pSAa8DQsSQlDSeAT9VB1sByU4Xe9Ze_uboSpvN2I3En7ZlzsFTUTWCVkoRhwMSMp54LWTQYvy7Ke6203pJwCicULzHx2NKpcziD9ydyEnE8lzRZaLmemKMkwCwlrdfY35Xcvh3vwd98Q";
            Stream dataStreamResponse = null;
            Stream dataStreamRequest = null;
            HttpWebResponse httpResponse = null;
            StreamReader reader = null;
            string responseFromServer = String.Empty;
            try
            {
                /************************************************************************
                * HTTP Request Header
                ************************************************************************/
                HttpWebRequest tRequest;
                tRequest = (HttpWebRequest)WebRequest.Create(GCM_SERVER_URI);
                tRequest.Method = "post";
                tRequest.ContentType = " application/x-www-form-urlencoded;charset=UTF-8";
                tRequest.Headers.Add(string.Format("Authorization: key={0}", _googleAppId));
                //tRequest.Headers.Add(string.Format("Sender: id={0}", _senderId)); //seems like that Sender parameter is optionally

                /************************************************************************
                 * HTTP Request Body
                 ************************************************************************/
                
                StringBuilder str = new StringBuilder();
                str.Append("collapse_key=score_update");
                str.Append("&time_to_live=108");
                str.Append("&delay_while_idle=1");
                str.Append("&data.msgcontent=" + message);
                str.Append("&data.msgid=" + notificationId);
                str.Append("&data.msgsender=" + "HASEEB");
                str.Append("&data.msgdate=" + DateTime.Now.Millisecond);
                str.Append("&registration_id=" + deviceId);

                Byte[] byteArray = Encoding.UTF8.GetBytes(str.ToString());
                tRequest.ContentLength = byteArray.Length;

                dataStreamRequest = tRequest.GetRequestStream();
                dataStreamRequest.Write(byteArray, 0, byteArray.Length);

                /************************************************************************
                 * HTTP Response
                 ************************************************************************/
                httpResponse = (HttpWebResponse)tRequest.GetResponse();                
                dataStreamResponse = httpResponse.GetResponseStream();
                reader = new StreamReader(dataStreamResponse);
                responseFromServer = reader.ReadToEnd();

            }
            catch (WebException e)
            {
                if (e.Status == WebExceptionStatus.ProtocolError)
                {
                    Console.WriteLine("Status Code : {0}", ((HttpWebResponse)e.Response).StatusCode);
                    Console.WriteLine("Status Description : {0}", ((HttpWebResponse)e.Response).StatusDescription);
                }

                //Todo: Error-Handling
                return false;
            }
            finally
            {
                /************************************************************************
                 * Cleanup
                 ************************************************************************/
                if(dataStreamResponse != null)
                    dataStreamResponse.Close();
                if (reader != null)
                    reader.Close();
                if (dataStreamRequest != null)
                    dataStreamRequest.Close();
                if (httpResponse != null)
                    httpResponse.Close();
            }

            if (responseFromServer.StartsWith("Error"))
            {
                GcmErrorHandler(responseFromServer);
                return false;
            }
                
            return true;
        }

        private void GcmErrorHandler(string serverResponse)
        {
            switch (serverResponse)
            {

                /*Check that the request contains a registration ID (either in the registration_id parameter in a plain text message, 
                  or in the registration_ids field in JSON).
                  Happens when error code is MissingRegistration.*/
                case "MissingRegistration":
                    Console.WriteLine("MissingRegistration");
                    break;
                
                /*Check the formatting of the registration ID that you pass to the server. 
                 Make sure it matches the registration ID the phone receives in the com.google.android.c2dm.intent.REGISTRATION intent 
                 and that you're not truncating it or adding additional characters. Happens when error code is InvalidRegistration.*/
                case "Error=InvalidRegistration":
                    Console.WriteLine("InvalidRegistration");
                    break;

                /*A registration ID is tied to a certain group of senders. When an application registers for GCM usage, 
                  it must specify which senders are allowed to send messages. Make sure you're using one of those when trying to send 
                  messages to the device. If you switch to a different sender, the existing registration IDs won't work. 
                  Happens when error code is MismatchSenderId.*/
                case "MismatchSenderId":
                    Console.WriteLine("MismatchSenderId");
                    break;
                
                /*An existing registration ID may cease to be valid in a number of scenarios, including: 
                    •If the application manually unregisters by issuing a com.google.android.c2dm.intent.UNREGISTER intent.
                    •If the application is automatically unregistered, which can happen (but is not guaranteed) if the user uninstalls the application.
                    •If the registration ID expires. Google might decide to refresh registration IDs. 
                    •If the application is updated but the new version does not have a broadcast receiver configured to receive com.google.android.c2dm.intent.RECEIVE intents.                
                 For all these cases, you should remove this registration ID from the 3rd-party server and stop using it to send messages.
                 Happens when error code is NotRegistered.*/
                case "NotRegistered":
                    Console.WriteLine("NotRegistered");
                    break;

                 /*The total size of the payload data that is included in a message can't exceed 4096 bytes. 
                   Note that this includes both the size of the keys as well as the values.*/
                case "MessageTooBig":
                    Console.WriteLine("MessageTooBig");
                    break;

                /*The payload data contains a key (such as from or any value prefixed by google.) that is used internally by GCM in 
                  the com.google.android.c2dm.intent.RECEIVE Intent and cannot be used. Note that some words (such as collapse_key) 
                  are also used by GCM but are allowed in the payload, in which case the payload value will be overridden by the GCM value.
                 */
                case "InvalidDataKey":
                    Console.WriteLine("InvalidDataKey");
                    break;
                
                /*The value for the Time to Live field must be an integer representing a duration in seconds between 0 and 2,419,200 (4 weeks). */
                case "InvalidTtl":
                    Console.WriteLine("InvalidTtl");
                    break;

                /*The server couldn't process the request in time. You should retry the same request, but you MUST obey the following requirements: 
                    •Honor the Retry-After header if it's included in the response from the GCM server.
                    •Implement exponential back-off in your retry mechanism. This means an exponentially increasing delay after each failed retry 
                     (e.g. if you waited one second before the first retry, wait at least two second before the next one, then 4 seconds and so on). 
                     If you're sending multiple messages, delay each one independently by an additional random amount to avoid issuing a 
                     new request for all messages at the same time. Senders that cause problems risk being blacklisted. */
            case "Unavailable":
                Console.WriteLine("Unavailable");
                break;

            /*The server encountered an error while trying to process the request. You could retry the same request 
              (obeying the requirements listed in the Timeout section), but if the error persists, 
              please report the problem in the android-gcm group.
              Senders that cause problems risk being blacklisted. 
              Happens when the HTTP status code is 500, or when the error field of a JSON object in the results array */
                case "InternalServerError":
                    Console.WriteLine("InternalServerError");
                    break;

                default:
                    break;
            }
        }
    }
}